<?php

declare(strict_types=1);

namespace IdaasClient\Middleware;

use App\Utils\IdaasUtil;
use App\Utils\UserTokenUtil;
use Hyperf\Context\Context;
use Hyperf\Grpc\StatusCode;
use Hyperf\GrpcServer\Exception\GrpcException;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Network;
use IdaasClient\Constants\IdaasConst;
use IdaasClient\Utils\IdaasClientUtil;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Grpc\Base\Idaas\V1\IdaasClient;

trait BaseMiddleware
{

    protected function getProjectId($request) {
        if (!empty($this->xProjectId)) {
            return $this->xProjectId;
        }
        
        // 优先从header里取
        $projectId = $request->getHeaderLine('x-jfb-project-id');
        if (!empty($projectId)) {
            return $projectId;
        }

        // header里没有从config里获取
        if (!empty($this->project) && isset($this->config['projects'][$this->project])) {
            $projectId = $this->config['projects'][$this->project]['project_id'] ?? '';
        }

        if (empty($projectId)) {
            throw new GrpcException('此项目未配置', StatusCode::INTERNAL);
        }

        return $projectId;
    }
    private function diffPermission($userPermissions, $routeResourceInfo, $permission) {
        foreach ($userPermissions as $userPermission) {
            // 处理免鉴权的角色
//            if (!empty($this->noCheckRoleIds)) {
//                if (\IdaasClient\Utils\IdaasUtil::inArray($this->noCheckRoleIds, $userPermission['role_ids'])) {
//                    return true;
//                }
//            }
            
            // 有这个权限的才允许
            if (!self::checkPermission($permission, $userPermission['permissions'])) {
                continue;
            }
            // 有资源信息时
            if (!empty($routeResourceInfo)) {
                // 匹配资源
                $userResources = $userPermission['resources'];
                foreach ($userResources as $userResource)  {
                    if ($this->diffOneResource($userResource, $routeResourceInfo)) {
                        return true;
                    }
                }
            } else {
                // 没有资源时直接通过
                return true;
            }
        }
        return false;
    }

    private static function checkPermission($permission, $data) {
        if (in_array('*', $data)) {
            return true;
        }
        // 没有这个权限则跳过
        if (in_array($permission, $data)) {
            return true;
        }
        return false;
    }

    private function parseDeptIds($userPermissions, $permission, $routeResourceInfo) {
        $deptIds = [];
        foreach ($userPermissions as $userPermission) {
            if (empty($userPermission['dept_ids'])) {
                continue;
            }
            // 没有这个权限则跳过
            if (!self::checkPermission($permission, $userPermission['permissions'])) {
                continue;
            }

            // 如果没有资源直接通过
            $userResources = $userPermission['resources'];
            if (empty($userResources)) {
                array_push($deptIds, ...$userPermission['dept_ids']);
                continue;
            }

            // 匹配资源
            foreach ($userResources as $userResource)  {
                if ($this->diffOneResource($userResource, $routeResourceInfo)) {
                    array_push($deptIds, ...$userPermission['dept_ids']);
                }
            }
        }
        $deptIds = array_unique($deptIds);
        $deptIds = array_filter($deptIds);
        Context::set(IdaasConst::DEPTIDS_CONTEXT_KEY, $deptIds);
    }

    private function diffOneResource(array $userAuthResource, array $routeResourceInfo) {
        $is = false;
        foreach ($routeResourceInfo as $key => $value) {
            if (!isset($userAuthResource[$key])) {
                return false;
            }
            if (is_array($userAuthResource[$key])) {
                if (in_array($value, $userAuthResource[$key])) {
                    $is = true;
                } else {
                    return false;
                }
            } else {
                if ($userAuthResource[$key] === $value || $userAuthResource[$key] === 0 || 0 === strcmp((string) $userAuthResource[$key], '*')) {
                    $is = true;
                } else {
                    return false;
                }
            }
        }

//        foreach ($userAuthResource as $key => $value) {
//            if (!isset($routeResourceInfo[$key])) {
//                return false;
//            }
//            if (is_array($value)) {
//                if (in_array($routeResourceInfo[$key], $value)) {
//                    $is = true;
//                } else {
//                    return false;
//                }
//            } else {
//                if ($routeResourceInfo[$key] === $value || $value === 0 || 0 === strcmp((string) $value, '*')) {
//                    $is = true;
//                } else {
//                    return false;
//                }
//            }
//        }
        
        return $is;
    }

    /**
     * 获取路由的权限值
     */
    private function getRoutePermission($request) {
        /* @var $dispatched Dispatched */
        $dispatched = $request->getAttribute(Dispatched::class);
        $handler = $dispatched->handler->options['handler'] ?? '';
        if (empty($handler)) {
            throw new GrpcException('没有配置没有权限值', StatusCode::INTERNAL);
        }
        return $handler;
    }

    private function getRouteInfo($request, $userInfo) {
        $routeInfo = [];
        /* @var $dispatched Dispatched */
        $dispatched = $request->getAttribute(Dispatched::class);
        foreach ($this->resources as $resource) {
            if (0 === strcmp($resource, 'partner_id')) {
                $routeInfo['partner_id'] = (int) $userInfo['current_partner_id'];
            } else if (0 === strcmp($resource, 'site_id')) {
                $routeInfo['site_id'] = (string) $dispatched->params['xsiteid'];
            } else if (0 === strcmp($resource, 'namespace')) {
                $routeInfo['namespace'] = (string) $dispatched->params['xnamespace'];
            }
        }
        return $routeInfo;
    }
    
    private function checkResource(int $partnerId, string $siteId, $namespace) {
        if ($partnerId < 1 || empty($siteId)) {
            return;
        }

        $client = IdaasClient::create();
        if (!method_exists($client, 'CheckResource')) {
            return;
        }
        
        return ApplicationContext::getContainer()->get(IdaasClientUtil::class)->checkResource(
            partnerId: $partnerId,
            siteId: $siteId,
            namespace: $namespace
        );
    }
    
    private function getUserPermissions(string $userId, string $sessionId) {
        return Context::getOrSet('admin-user-permissons', function () use ($userId, $sessionId) {
            $poolId = $this->config['pool_id'];
            $appId = $this->config['app_id'];
            $appSecret = $this->config['app_secret'];
            return ApplicationContext::getContainer()->get(IdaasClientUtil::class)->getUserPermissions(
                poolId: $poolId,
                projectId: $this->projectId,
                appId: $appId,
                appSecret: $appSecret,
                userId: $userId,
                sessionId: $sessionId,
            );
        });
        return $data;
    }

    private function getPool() {
        $host = ApplicationContext::getContainer()->get(RequestInterface::class)->getUri()->getHost();
        $key = sprintf('pool-%s-%s', $this->poolId, $host);
        $data = Context::getOrSet($key, function () use ($host) {
            return ApplicationContext::getContainer()->get(IdaasClientUtil::class)->getPool(
                poolId: $this->poolId, 
                domain: $host
            );
        });
        return $data;
    }

    private function getAccessToken() {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        $pool = $this->getPool();
        $token = $request->cookie($pool['access_token_key']);
        if (empty($token)) {
            throw new GrpcException('用户未登录或已失效，请重新登录', StatusCode::UNAUTHENTICATED);
        }
        return $token;
    }

    private function getCsrfToken() {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        $token = $request->header('jfb-csrf-token');
        if (empty($token)) {
            throw new GrpcException('csrf_token不能为空', StatusCode::UNAUTHENTICATED);
        }
        return $token;
    }

    private function getUserInfo(ServerRequestInterface $request) {
        return Context::getOrSet(IdaasConst::USERINFO_CONTEXT_KEY, function () {
            $accessToken = $this->getAccessToken();
            $csrfToken = $this->getCsrfToken();
            return ApplicationContext::getContainer()->get(IdaasClientUtil::class)->parseToken($accessToken, $csrfToken, $this->poolId);
        });
    }

    private function checkUserType(array $userInfo) {
        $userType = $userInfo['user_type'] ?? '';
        if (empty($userType)) {
            throw new GrpcException('用户类型异常，请重新登录', StatusCode::PERMISSION_DENIED);
        }
        if (!in_array($userType, $this->allowUserTypes)) {
            throw new GrpcException('您没有权限访问', StatusCode::PERMISSION_DENIED);
        }
    }
}
